package cn.hashq.netpostclient.util;

import cn.hashq.netpostclient.cache.ConnectionInfo;
import cn.hashq.netpostclient.dto.ProtoMsg;
import cn.hashq.netpostclient.handler.ExceptionHandler;
import cn.hashq.netpostclient.handler.ProtobufDecoder;
import cn.hashq.netpostclient.handler.ProtobufEncoder;
import cn.hashq.netpostclient.handler.proxy.AuthResponseHandler;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Slf4j
public class NettyUtil {

    private static AuthResponseHandler authResponseHandler = new AuthResponseHandler();

    private static ExceptionHandler exceptionHandler = new ExceptionHandler();

    private static ChannelInitializer<SocketChannel> channelInitializer = new ChannelInitializer<SocketChannel>() {
        @Override
        protected void initChannel(SocketChannel ch) throws Exception {
            ch.pipeline().addLast("encoder", new ProtobufEncoder())
                    .addLast("decoder", new ProtobufDecoder())
                    .addLast("auth", authResponseHandler)
                    .addLast(exceptionHandler);
        }
    };

    private static GenericFutureListener futureListener = new GenericFutureListener() {
        @Override
        public void operationComplete(Future future) throws Exception {
            if (future.isSuccess()) {
                log.info("连接代理端口成功");
            } else {
                log.error("连接代理端口失败,原因:{}", future.cause().getMessage());
                log.info("正在尝试重连....");
                connectServer(ConnectionInfo.getInstance());
            }
        }
    };

    public static ChannelFuture connectServer(String host, int port, ChannelInitializer<SocketChannel> channels, GenericFutureListener listener) {
        try {
            EventLoopGroup bg = new NioEventLoopGroup(1);
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(bg);
            bootstrap.remoteAddress(host, port);
            bootstrap.channel(NioSocketChannel.class);
            bootstrap.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
            bootstrap.handler(channels);
            ChannelFuture future = bootstrap.connect().sync();
            future.addListener(listener);
            return future;
        } catch (InterruptedException e) {
            log.error("连接{}:{}失败,原因:{}", host, port, e.getMessage());
        }
        return null;
    }

    public static void connectServer(ConnectionInfo connectionInfo) {
        String proxyUrl = connectionInfo.getProxyUrl();
        String[] serverInfo = proxyUrl.split(":");
        String secret = connectionInfo.getSecret();
        connectServer(serverInfo, secret);
    }

    public static void connectServer(String[] serverInfo,
                                     String secret) {
        ChannelFuture channelFuture = null;
        try {
            channelFuture = connectServer(serverInfo[0], Integer.valueOf(serverInfo[1]), channelInitializer, futureListener);
        } catch (Exception e) {
            log.info("正在尝试重连...");
            delayConnect();
            return;
        }
        if (Objects.isNull(channelFuture)) {
            log.info("正在尝试重连...");
            delayConnect();
        } else {
            ProtoMsg.Auth auth = ProtoMsg.Auth.newBuilder()
                    .setSecret(secret).build();
            ProtoMsg.Message msg = ProtoMsg.Message.newBuilder()
                    .setType(ProtoMsg.HeadType.AUTH)
                    .setAuth(auth)
                    .build();
            channelFuture.channel().writeAndFlush(msg);
            channelFuture.channel().closeFuture().addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    log.info("正在尝试重连...");
                    delayConnect();
                }
            });
        }
    }

    public static void delayConnect() {
        ThreadUtil.getSeqOrScheduledExecutorService().schedule(() -> {
            connectServer(ConnectionInfo.getInstance());
        }, 3, TimeUnit.SECONDS);
    }
}
